前言 MediaScanner是Android系统中针对媒体文件的扫描过程,将储存空间中的媒体文件通过扫描的方式遍历并存储在数据库中,然后通过MediaProvider提供接口使用,在Android多媒体中占有很重要的位置。
源码位置 packages\providers\mediaprovider frameworks\base\media\java\android\media frameworks\av\media\libmedia frameworks\base\media\jni frameworks\av\media\libstagefright
扫描过程 MediaScannerReceiver.java (packages\providers\mediaprovider\src\com\android\providers\media) MediaScannerReceiver继承BroadcastReceiver,在AndroidManifest.xml静态注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <receiver android:name ="MediaScannerReceiver" > <intent-filter > <action android:name ="android.intent.action.BOOT_COMPLETED" /> </intent-filter > <intent-filter > <action android:name ="android.intent.action.MEDIA_MOUNTED" /> <data android:scheme ="file" /> </intent-filter > <intent-filter > <action android:name ="android.intent.action.MEDIA_UNMOUNTED" /> <data android:scheme ="file" /> </intent-filter > <intent-filter > <action android:name ="android.intent.action.MEDIA_SCANNER_SCAN_FILE" /> <data android:scheme ="file" /> </intent-filter > </receiver >
可以看到,当接收到 android.intent.action.BOOT_COMPLETED android.intent.action.MEDIA_MOUNTED android.intent.action.MEDIA_UNMOUNTED android.intent.action.MEDIA_SCANNER_SCAN_FILE 广播的时候,MediaScannerReceiver中的onReceiver()方法将会执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public void onReceive (Context context, Intent intent) { final String action = intent.getAction(); final Uri uri = intent.getData(); if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { scan(context, MediaProvider.INTERNAL_VOLUME); scan(context, MediaProvider.EXTERNAL_VOLUME); } else { if (uri.getScheme().equals("file" )) { String path = uri.getPath(); String externalStoragePath = Environment.getExternalStorageDirectory().getPath(); String legacyPath = Environment.getLegacyExternalStorageDirectory().getPath(); try { path = new File(path).getCanonicalPath(); } catch (IOException e) { Log.e(TAG, "couldn't canonicalize " + path); return ; } if (path.startsWith(legacyPath)) { path = externalStoragePath + path.substring(legacyPath.length()); } Log.d(TAG, "action: " + action + " path: " + path); if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) { scan(context, MediaProvider.EXTERNAL_VOLUME); } else if (Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action) && path != null && path.startsWith(externalStoragePath + "/" )) { scanFile(context, path); } } } }
onReceiver方法中,会执行到scan()或者是scanFile()方法,一个是针对整个卷,一个是针对具体的文件,从传入的参数可以看出眉目,scan传入的是卷名,scanFile传入的是文件路径。通过接收广播启动扫描,然而不管是scan还是scanFile都是以startService的方式启动MediaScannerService。
1 2 3 4 5 6 7 8 9 10 11 12 13 private void scan (Context context, String volume) { Bundle args = new Bundle(); args.putString("volume" , volume); context.startService( new Intent(context, MediaScannerService.class).putExtras(args)); } private void scanFile (Context context, String path) { Bundle args = new Bundle(); args.putString("filepath" , path); context.startService( new Intent(context, MediaScannerService.class).putExtras(args)); }
分别传入卷名或者文件路径。 MediaScannerService.java (packages\providers\mediaprovider\src\com\android\providers\media) MediaScannerService看下AndroidManifest.xml文件中定义
1 2 3 4 5 <service android:name ="MediaScannerService" android:exported ="true" > <intent-filter > <action android:name ="android.media.IMediaScannerService" /> </intent-filter > </service >
继承Service,实现Runnable接口,首先执行onCreate()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 public void onCreate () { PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); StorageManager storageManager = (StorageManager)getSystemService(Context.STORAGE_SERVICE); mExternalStoragePaths = storageManager.getVolumePaths(); Thread thr = new Thread(null , this , "MediaScannerService" ); thr.start(); }
这里会单独启动一个线程来处理,然后走到onStartCommand()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public int onStartCommand (Intent intent, int flags, int startId) { while (mServiceHandler == null ) { synchronized (this ) { try { wait(100 ); } catch (InterruptedException e) { } } } if (intent == null ) { Log.e(TAG, "Intent is null in onStartCommand: " , new NullPointerException()); return Service.START_NOT_STICKY; } Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent.getExtras(); mServiceHandler.sendMessage(msg); return Service.START_REDELIVER_INTENT; }
这里先等待mServiceHandler创建完成,然后通过handler message机制传递消息给handler处理,这里的mServiceHandler创建过程是在run()方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 public void run () { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_LESS_FAVORABLE); Looper.prepare(); mServiceLooper = Looper.myLooper(); mServiceHandler = new ServiceHandler(); Looper.loop(); }
由于这个是单独的线程,无法保证在发送消息的时候,handler已经创建完成,所以需要等待handler的创建 看下这个处理消息的handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 private final class ServiceHandler extends Handler { @Override public void handleMessage (Message msg) { Bundle arguments = (Bundle) msg.obj; String filePath = arguments.getString("filepath" ); try { if (filePath != null ) { IBinder binder = arguments.getIBinder("listener" ); IMediaScannerListener listener = (binder == null ? null : IMediaScannerListener.Stub.asInterface(binder)); Uri uri = null ; try { uri = scanFile(filePath, arguments.getString("mimetype" )); } catch (Exception e) { Log.e(TAG, "Exception scanning file" , e); } if (listener != null ) { listener.scanCompleted(filePath, uri); } } else { String volume = arguments.getString("volume" ); String[] directories = null ; if (MediaProvider.INTERNAL_VOLUME.equals(volume)) { directories = new String[] { Environment.getRootDirectory() + "/media" , Environment.getOemDirectory() + "/media" , }; } else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) { directories = mExternalStoragePaths; } if (directories != null ) { if (false ) Log.d(TAG, "start scanning volume " + volume + ": " + Arrays.toString(directories)); scan(directories, volume); if (false ) Log.d(TAG, "done scanning volume " + volume); } } } catch (Exception e) { Log.e(TAG, "Exception in handleMessage" , e); } stopSelf(msg.arg1); } };
分别执行scan和scanFile方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 private void scan (String[] directories, String volumeName) { Uri uri = Uri.parse("file://" + directories[0 ]); mWakeLock.acquire(); try { ContentValues values = new ContentValues(); values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName); Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values); sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri)); try { if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) { openDatabase(volumeName); } MediaScanner scanner = createMediaScanner(); scanner.scanDirectories(directories, volumeName); } catch (Exception e) { Log.e(TAG, "exception in MediaScanner.scan()" , e); } getContentResolver().delete(scanUri, null , null ); } finally { sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri)); mWakeLock.release(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 private Uri scanFile (String path, String mimeType) { String volumeName = MediaProvider.EXTERNAL_VOLUME; openDatabase(volumeName); MediaScanner scanner = createMediaScanner(); try { String canonicalPath = new File(path).getCanonicalPath(); return scanner.scanSingleFile(canonicalPath, volumeName, mimeType); } catch (Exception e) { Log.e(TAG, "bad path " + path + " in scanFile()" , e); return null ; } }
两者都是会通过createMediaScanner创建MediaScanner对象,然后通过scanDirectories或者scanSingleFile来对卷或者文件进行扫描,下面我们就只针对scanDirectories进行分析。
MediaScanner.java (base\media\java\android\media)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public void scanDirectories (String[] directories, String volumeName) { try { long start = System.currentTimeMillis(); initialize(volumeName); prescan(null , true ); long prescan = System.currentTimeMillis(); if (ENABLE_BULK_INSERTS) { mMediaInserter = new MediaInserter(mMediaProvider, mPackageName, 500 ); } for (int i = 0 ; i < directories.length; i++) { processDirectory(directories[i], mClient); } if (ENABLE_BULK_INSERTS) { mMediaInserter.flushAll(); mMediaInserter = null ; } long scan = System.currentTimeMillis(); postscan(directories); long end = System.currentTimeMillis(); if (false ) { Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n" ); Log.d(TAG, " scan time: " + (scan - prescan) + "ms\n" ); Log.d(TAG, "postscan time: " + (end - scan) + "ms\n" ); Log.d(TAG, " total time: " + (end - start) + "ms\n" ); } } catch (SQLException e) { Log.e(TAG, "SQLException in MediaScanner.scan()" , e); } catch (UnsupportedOperationException e) { Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()" , e); } catch (RemoteException e) { Log.e(TAG, "RemoteException in MediaScanner.scan()" , e); } finally { releaseResources(); } }
这里面比较重要的几个内容,一个是prescan,一个是processDirectory 先看看prescan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit" , "1000" ).build(); mWasEmptyPriorToScan = true ; while (true ) { selectionArgs[0 ] = "" + lastId; if (c != null ) { c.close(); c = null ; } c = mMediaProvider.query(mPackageName, limitUri, FILES_PRESCAN_PROJECTION, where, selectionArgs, MediaStore.Files.FileColumns._ID, null ); if (c == null ) { break ; } int num = c.getCount(); if (num == 0 ) { break ; } mWasEmptyPriorToScan = false ; while (c.moveToNext()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); lastId = rowId; if (path != null && path.startsWith("/" )) { boolean exists = false ; try { exists = Os.access(path, android.system.OsConstants.F_OK); } catch (ErrnoException e1) { } ...... } } } } }
这里主要就是注意下最后修改时间,后面扫描的过程中应该会用到。 再看下processDirectory,native方法 android_media_MediaScanner.cpp (base\media\jni)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static void android_media_MediaScanner_processDirectory( JNIEnv *env, jobject thiz, jstring path, jobject client) { ALOGV("processDirectory" ); MediaScanner *mp = getNativeScanner_l(env, thiz); if (mp == NULL ) { jniThrowException(env, kRunTimeException, "No scanner available" ); return ; } if (path == NULL ) { jniThrowException(env, kIllegalArgumentException, NULL ); return ; } const char *pathStr = env->GetStringUTFChars(path, NULL ); if (pathStr == NULL ) { return ; } MyMediaScannerClient myClient (env, client) ; MediaScanResult result = mp->processDirectory(pathStr, myClient); if (result == MEDIA_SCAN_RESULT_ERROR) { ALOGE("An error occurred while scanning directory '%s'." , pathStr); } env->ReleaseStringUTFChars(path, pathStr); }
从MediaScanner.java一直跟过来,忘记了MediaScanner.java中有一个比较重要的内容
1 2 3 4 static { System.loadLibrary("media_jni" ); native_init(); }
这里加载libmedia_jni.so即上面会用到的android_media_MediaScanner.cpp就是存在于这个so中,
1 private static native final void native_init () ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static void android_media_MediaScanner_native_init(JNIEnv *env) { ALOGV("native_init" ); jclass clazz = env->FindClass(kClassMediaScanner); if (clazz == NULL ) { return ; } fields.context = env->GetFieldID(clazz, "mNativeContext" , "J" ); if (fields.context == NULL ) { return ; } }
在创建MediaScanner对象的过程中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public MediaScanner (Context c) { native_setup(); mContext = c; mPackageName = c.getPackageName(); mBitmapOptions.inSampleSize = 1 ; mBitmapOptions.inJustDecodeBounds = true ; setDefaultRingtoneFileNames(); mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mExternalIsEmulated = Environment.isExternalStorageEmulated(); } ...... private native final void native_setup () ;
1 2 3 4 5 6 7 8 9 10 11 12 13 static void android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) { ALOGV("native_setup" ); MediaScanner *mp = new StagefrightMediaScanner; if (mp == NULL ) { jniThrowException(env, kRunTimeException, "Out of memory" ); return ; } env->SetLongField(thiz, fields.context, (jlong)mp); }
再回到processDirectory方法中来, MediaScanner *mp = getNativeScanner_l(env, thiz);拿到MediaScaner的cpp对象 MyMediaScannerClient myClient(env, client); //创建MyMediaScannerClient对象,而这里传入的client是MediaScanner.java中的MyMediaScannerClient对象 MediaScanResult result = mp->processDirectory(pathStr, myClient); //执行processDirectory方法,传入myClient对象 这样传来传去无非就是想保存下当前的java侧的一个client,后面会有妙用? StagefrightMediaScanner继承MediaScanner,没有实现processDirectory方法,使用的是父类中的方法
1 2 3 4 5 6 7 8 MediaScanResult MediaScanner::processDirectory( const char *path, MediaScannerClient &client) { ...... MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, false ); free (pathBuffer); return result; }
1 2 3 4 5 6 7 8 9 10 11 12 MediaScanResult MediaScanner::doProcessDirectory( char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) { ...... while ((entry = readdir(dir))) { if (doProcessDirectoryEntry(path, pathRemaining, client, noMedia, entry, fileSpot) == MEDIA_SCAN_RESULT_ERROR) { result = MEDIA_SCAN_RESULT_ERROR; break ; } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 MediaScanResult MediaScanner::doProcessDirectoryEntry( char *path, int pathRemaining, MediaScannerClient &client, bool noMedia, struct dirent* entry, char * fileSpot) { ...... if (type == DT_DIR) { bool childNoMedia = noMedia; if (name[0 ] == '.' ) childNoMedia = true ; if (stat(path, &statbuf) == 0 ) { status_t status = client.scanFile(path, statbuf.st_mtime, 0 , true , childNoMedia); if (status) { return MEDIA_SCAN_RESULT_ERROR; } } strcat (fileSpot, "/" ); MediaScanResult result = doProcessDirectory(path, pathRemaining - nameLength - 1 , client, childNoMedia); if (result == MEDIA_SCAN_RESULT_ERROR) { return MEDIA_SCAN_RESULT_ERROR; } } else if (type == DT_REG) { stat(path, &statbuf); status_t status = client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false , noMedia); ...... }
这里注意一下,使用的client就是之前传入的参数,最终会调用会MediaScanner.java中MyMediaScannerClient中的scanFile方法
1 2 3 4 5 6 public void scanFile (String path, long lastModified, long fileSize, boolean isDirectory, boolean noMedia) { doScanFile(path, null, lastModified, fileSize, isDirectory, false , noMedia); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public Uri doScanFile (String path, String mimeType, long lastModified, long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) { ...... try { FileEntry entry = beginFile(path, mimeType, lastModified, fileSize, isDirectory, noMedia); ...... if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { if (noMedia) { result = endFile(entry, false , false , false , false , false ); } else { ...... if (isaudio || isvideo) { processFile(path, mimeType, this ); } if (isimage) { processImageFile(path); } result = endFile(entry, ringtones, notifications, alarms, music, podcasts); } ...... }
下面看下音频文件的处理 processFile(path, mimeType, this),注意这里传入的参数有路径和mimetype
1 private native void processFile (String path, String mimeType, MediaScannerClient client) ;
跟到android_media_MediaScanner.cpp
1 2 MyMediaScannerClient myClient (env, client) ;MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
然后跟到 StagefrightMediaScanner.cpp (av\include\media\stagefright)
1 2 3 4 5 6 7 8 9 10 11 MediaScanResult StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { ALOGV("processFile '%s'." , path); client.setLocale(locale()); client.beginFile(); MediaScanResult result = processFileInternal(path, mimeType, client); client.endFile(); return result; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 MediaScanResult StagefrightMediaScanner::processFileInternal( const char *path, const char * , MediaScannerClient &client) { ...... sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); int fd = open(path, O_RDONLY | O_LARGEFILE); status_t status; if (fd < 0 ) { status = mRetriever->setDataSource(NULL , path); } else { status = mRetriever->setDataSource(fd, 0 , 0x7ffffffffffffff L); close(fd); } ...... const char *value; if ((value = mRetriever->extractMetadata( METADATA_KEY_MIMETYPE)) != NULL ) { status = client.setMimeType(value); if (status) { return MEDIA_SCAN_RESULT_ERROR; } } struct KeyMap { const char *tag; int key; }; static const KeyMap kKeyMap[] = { { "tracknumber" , METADATA_KEY_CD_TRACK_NUMBER }, { "discnumber" , METADATA_KEY_DISC_NUMBER }, { "album" , METADATA_KEY_ALBUM }, { "artist" , METADATA_KEY_ARTIST }, { "albumartist" , METADATA_KEY_ALBUMARTIST }, { "composer" , METADATA_KEY_COMPOSER }, { "genre" , METADATA_KEY_GENRE }, { "title" , METADATA_KEY_TITLE }, { "year" , METADATA_KEY_YEAR }, { "duration" , METADATA_KEY_DURATION }, { "writer" , METADATA_KEY_WRITER }, { "compilation" , METADATA_KEY_COMPILATION }, { "isdrm" , METADATA_KEY_IS_DRM }, { "width" , METADATA_KEY_VIDEO_WIDTH }, { "height" , METADATA_KEY_VIDEO_HEIGHT }, }; static const size_t kNumEntries = sizeof (kKeyMap) / sizeof (kKeyMap[0 ]); for (size_t i = 0 ; i < kNumEntries; ++i) { const char *value; if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL ) { status = client.addStringTag(kKeyMap[i].tag, value); if (status != OK) { return MEDIA_SCAN_RESULT_ERROR; } } } return MEDIA_SCAN_RESULT_OK; }
这里创建的MediaMetadataRetriever并不是MediaMetadataRetriver本身,而是一个之类,StagefrightMetadataRetriver,中间有一个过程,我们来看下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 MediaMetadataRetriever::MediaMetadataRetriever() { ALOGV("constructor" ); const sp<IMediaPlayerService>& service(getService()); if (service == 0 ) { ALOGE("failed to obtain MediaMetadataRetrieverService" ); return ; } sp<IMediaMetadataRetriever> retriever(service->createMetadataRetriever()); if (retriever == 0 ) { ALOGE("failed to create IMediaMetadataRetriever object from server" ); } mRetriever = retriever; }
这里的service是MediaPlayerService,这里使用的代理,实现直接看MediaPlayerService.cpp中的函数
1 2 3 4 5 6 7 8 sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever() { pid_t pid = IPCThreadState::self()->getCallingPid(); sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid); ALOGV("Create new media retriever from pid %d" , pid); return retriever; }
MetadataRetrieverClient.cpp (av\media\libmediaplayerservice)
1 2 3 4 5 6 7 8 MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid) { ALOGV("MetadataRetrieverClient constructor pid(%d)" , pid); mPid = pid; mThumbnail = NULL ; mAlbumArt = NULL ; mRetriever = NULL ; }
显然上面创建的东西只是一个空壳,继续往下看 在processFileInternal函数中setDataSource函数中同样会跟到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t length){ ...... player_type playerType = MediaPlayerFactory::getPlayerType(NULL , fd, offset, length); ALOGV("player type = %d" , playerType); sp<MediaMetadataRetrieverBase> p = createRetriever(playerType); if (p == NULL ) { ::close(fd); return NO_INIT; } status_t status = p->setDataSource(fd, offset, length); if (status == NO_ERROR) mRetriever = p; ::close(fd); return status; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType){ sp<MediaMetadataRetrieverBase> p; switch (playerType) { case STAGEFRIGHT_PLAYER: case NU_PLAYER: { p = new StagefrightMetadataRetriever; break ; } case SONIVOX_PLAYER: ALOGV("create midi metadata retriever" ); p = new MidiMetadataRetriever(); break ; default : ALOGE("player type %d is not supported" , playerType); break ; } if (p == NULL ) { ALOGE("failed to create a retriever object" ); } return p; }
StagefrightMetadataRetriever.cpp (av\media\libstagefright) 那么然后执行setDataSouce()
1 2 3 4 5 6 7 status_t StagefrightMetadataRetriever::setDataSource( int fd, int64_t offset, int64_t length) { ...... mExtractor = MediaExtractor::Create(mSource); ...... }
setDataSouce()有两个方法,传入的参数不用,另外一个是针对在线媒体的 然后就是创建媒体解析器 MediaExtractor.cpp (av\media\libstagefright)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 sp<MediaExtractor> MediaExtractor::Create( const sp<DataSource> &source, const char *mime) { ...... if (mime == NULL ) { float confidence; if (!source->sniff(&tmp, &confidence, &meta)) { ALOGV("FAILED to autodetect media content." ); return NULL ; } mime = tmp.string (); ALOGV("Autodetected media content as '%s' with confidence %.2f" , mime, confidence); } ...... MediaExtractor *ret = NULL ; if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) || !strcasecmp(mime, "audio/mp4" )) { ret = new MPEG4Extractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { ret = new MP3Extractor(source, meta); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { ret = new AMRExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) { ret = new FLACExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) { ret = new WAVExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) { ret = new OggExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) { ret = new MatroskaExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { ret = new MPEG2TSExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) { return new WVMExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) { ret = new AACExtractor(source, meta); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) { ret = new MPEG2PSExtractor(source); } if (ret != NULL ) { if (isDrm) { ret->setDrmFlag(true ); } else { ret->setDrmFlag(false ); } } return ret; }
针对mimetype为空的情况 DataSource.cpp (av\media\libstagefright)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 bool DataSource::sniff( String8 *mimeType, float *confidence, sp<AMessage> *meta) { *mimeType = "" ; *confidence = 0.0f ; meta->clear(); { Mutex::Autolock autoLock (gSnifferMutex) ; if (!gSniffersRegistered) { return false ; } } for (List<SnifferFunc>::iterator it = gSniffers.begin(); it != gSniffers.end(); ++it) { String8 newMimeType; float newConfidence; sp<AMessage> newMeta; if ((*it)(this , &newMimeType, &newConfidence, &newMeta)) { if (newConfidence > *confidence) { *mimeType = newMimeType; *confidence = newConfidence; *meta = newMeta; } } } return *confidence > 0.0 ; }
到这里对应的mimetype和extractor都明确了,再回到StagefrightMediaScanner.cpp中processFileInternal函数,执行完了setDataSouce,执行extractMetadata,依然是跟到StagefrightMetadataRetriver.cpp中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) { if (mExtractor == NULL ) { return NULL ; } if (!mParsedMetaData) { parseMetaData(); mParsedMetaData = true ; } ssize_t index = mMetaData.indexOfKey(keyCode); if (index < 0 ) { return NULL ; } return mMetaData.valueAt(index).string (); }
再看下parseMetaData函数,这个函数比较长,主要看下一部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 static const Map kMap[] = { { kKeyMIMEType, METADATA_KEY_MIMETYPE, NULL }, { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER, "tracknumber" }, { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER, "discnumber" }, { kKeyAlbum, METADATA_KEY_ALBUM, "album" }, { kKeyArtist, METADATA_KEY_ARTIST, "artist" }, { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST, "albumartist" }, { kKeyAuthor, METADATA_KEY_AUTHOR, NULL }, { kKeyComposer, METADATA_KEY_COMPOSER, "composer" }, { kKeyDate, METADATA_KEY_DATE, NULL }, { kKeyGenre, METADATA_KEY_GENRE, "genre" }, { kKeyTitle, METADATA_KEY_TITLE, "title" }, { kKeyYear, METADATA_KEY_YEAR, "year" }, { kKeyWriter, METADATA_KEY_WRITER, "writer" }, { kKeyCompilation, METADATA_KEY_COMPILATION, "compilation" }, { kKeyLocation, METADATA_KEY_LOCATION, NULL }, }; static const size_t kNumMapEntries = sizeof (kMap) / sizeof (kMap[0 ]);CharacterEncodingDetector *detector = new CharacterEncodingDetector(); for (size_t i = 0 ; i < kNumMapEntries; ++i) { const char *value; if (meta->findCString(kMap[i].from, &value)) { if (kMap[i].name) { detector->addTag(kMap[i].name, value); } else { mMetaData.add(kMap[i].to, String8(value)); } } } detector->detectAndConvert(); int size = detector->size();if (size) { for (int i = 0 ; i < size; i++) { const char *name; const char *value; detector->getTag(i, &name, &value); for (size_t j = 0 ; j < kNumMapEntries; ++j) { if (kMap[j].name && !strcmp (kMap[j].name, name)) { mMetaData.add(kMap[j].to, String8(value)); } } } }
此函数中其他的内容也是解析一些其他的元数据报保存下来。 再回到processFileInternal中,在解析完元数据之后
1 2 3 4 5 6 7 8 9 for (size_t i = 0 ; i < kNumEntries; ++i) { const char *value; if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL ) { status = client.addStringTag(kKeyMap[i].tag, value); if (status != OK) { return MEDIA_SCAN_RESULT_ERROR; } } }
将数据一个一个通过client的addStringTag往上传递,跟下此过程 MediaScannerClient.cpp (av\media\libmedia)
1 2 3 4 5 status_t MediaScannerClient::addStringTag(const char * name, const char * value){ handleStringTag(name, value); return OK; }
最后会回到MediaScanner.java中的handleStringTag()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public void handleStringTag (String name, String value) { if (name.equalsIgnoreCase("title" ) || name.startsWith("title;" )) { mTitle = value; } else if (name.equalsIgnoreCase("artist" ) || name.startsWith("artist;" )) { mArtist = value.trim(); } else if (name.equalsIgnoreCase("albumartist" ) || name.startsWith("albumartist;" ) || name.equalsIgnoreCase("band" ) || name.startsWith("band;" )) { mAlbumArtist = value.trim(); } else if (name.equalsIgnoreCase("album" ) || name.startsWith("album;" )) { mAlbum = value.trim(); } else if (name.equalsIgnoreCase("composer" ) || name.startsWith("composer;" )) { mComposer = value.trim(); } else if (mProcessGenres && (name.equalsIgnoreCase("genre" ) || name.startsWith("genre;" ))) { mGenre = getGenreName(value); } else if (name.equalsIgnoreCase("year" ) || name.startsWith("year;" )) { mYear = parseSubstring(value, 0 , 0 ); } else if (name.equalsIgnoreCase("tracknumber" ) || name.startsWith("tracknumber;" )) { int num = parseSubstring(value, 0 , 0 ); mTrack = (mTrack / 1000 ) * 1000 + num; } else if (name.equalsIgnoreCase("discnumber" ) || name.equals("set" ) || name.startsWith("set;" )) { int num = parseSubstring(value, 0 , 0 ); mTrack = (num * 1000 ) + (mTrack % 1000 ); } else if (name.equalsIgnoreCase("duration" )) { mDuration = parseSubstring(value, 0 , 0 ); } else if (name.equalsIgnoreCase("writer" ) || name.startsWith("writer;" )) { mWriter = value.trim(); } else if (name.equalsIgnoreCase("compilation" )) { mCompilation = parseSubstring(value, 0 , 0 ); } else if (name.equalsIgnoreCase("isdrm" )) { mIsDrm = (parseSubstring(value, 0 , 0 ) == 1 ); } else if (name.equalsIgnoreCase("width" )) { mWidth = parseSubstring(value, 0 , 0 ); } else if (name.equalsIgnoreCase("height" )) { mHeight = parseSubstring(value, 0 , 0 ); } else { } }
对应的数据已经被传递到了Java侧,剩下的就比较好处理了 本文只是跟了一条大致的代码流程,先到此,后续有可能还要更新一下。
本文中代码使用的是Android5.1原始代码,欢迎大家留言交流。